﻿using MultiLang;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Schema;

namespace Rsdn.Janus
{
    public abstract class ResourceCommandProvider : ICommandProvider
    {
        private const string _commandDescriptorSchemaResource =
            "Rsdn.Janus.CoreServices.Commands.JanusCommandDescriptor.xsd"; //MLHIDE

        private static readonly XmlReaderSettings _readerSettings;

        private readonly string _menuResourceName;
        private readonly ResourceManager _resourceManager;

        /// <summary>
        /// Initializes a new instance of the <see cref="ResourceCommandProvider"/> class.
        /// </summary>
        /// <param name="commandResourceName">Name of the command resource.</param>
        /// <param name="stringResourceName">Name of the string resource.</param>
        /// <exception cref="ArgumentException">Аргумент не должен быть null или пустой строкой.</exception>
        protected ResourceCommandProvider(
            [Localizable(false)] string commandResourceName,
            [Localizable(false)] string stringResourceName)
        {
            if (string.IsNullOrEmpty(commandResourceName))
            {
                throw new ArgumentException(ml.ml_string(42, "Аргумент не должен быть null или пустой строкой."), 
                    "commandResourceName");                           //MLHIDE
            }

            if (string.IsNullOrEmpty(stringResourceName))
            {
                throw new ArgumentException(ml.ml_string(42, "Аргумент не должен быть null или пустой строкой."),  
                    "commandResourceName");                           //MLHIDE
            }

            _menuResourceName = commandResourceName;
            _resourceManager = new ResourceManager(stringResourceName, GetType().Assembly);
        }

        /// <summary>
        /// Initializes the <see cref="ResourceCommandProvider"/> class.
        /// </summary>
        static ResourceCommandProvider()
        {
            _readerSettings = new XmlReaderSettings();
            _readerSettings.ValidationType = ValidationType.Schema;
            _readerSettings.Schemas.Add(CommonTypesSchema.Schema);

            Assembly assembly = typeof(ResourceMenuProvider).Assembly;
            Stream resourceStream = assembly.GetRequiredResourceStream(_commandDescriptorSchemaResource);
            XmlSchema xmlSchema = XmlSchema.Read(resourceStream, null);

            _readerSettings.Schemas.Add(xmlSchema);
        }

        #region ICommandProvider Members

        /// <summary>
        /// Создает команды.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<ICommandInfo> CreateCommands()
        {
            Stream resourceStream = GetType().Assembly.GetRequiredResourceStream(_menuResourceName);
            XmlReader xmlReader = XmlReader.Create(resourceStream, _readerSettings);
            IEnumerable<XElement> elements = XElement.Load(xmlReader).Elements();
            return elements.Select<XElement, ICommandInfo>(CreateCommand);
        }

        #endregion

        /// <summary>
        /// Creates the command.
        /// </summary>
        /// <param name="commandElement">The command element.</param>
        /// <returns></returns>
        private CommandInfo CreateCommand(XElement commandElement)
        {
            var commandName = (string)commandElement.Attribute(XName.Get(@"name")); //MLHIDE
            CommandType commandType = (CommandType)Enum.Parse(
                typeof(CommandType), (string)commandElement.Attribute(XName.Get(@"type"))); //MLHIDE

            Func<XElement, ICommandParameterInfo> selector =
                parameterElement =>
                {
                    XAttribute attributeName = parameterElement.Attribute(XName.Get(@"name")); //MLHIDE
                    XAttribute attributeOptional = parameterElement.Attribute(XName.Get(@"isOptional")); //MLHIDE
                    XAttribute attributeDescription1 = parameterElement.Attribute(XName.Get(@"description")); //MLHIDE
                    return new CommandParameterInfo(
                        (string)attributeName,
                        (bool)attributeOptional,
                        _resourceManager.GetDisplayString((string)attributeDescription1));
                };

            IEnumerable<ICommandParameterInfo> commandParameterInfos = commandElement.Elements().Select<XElement, ICommandParameterInfo>(selector);

            XAttribute attributeDisplayName = commandElement.Attribute(XName.Get(@"displayNameTextResource")); //MLHIDE
            XAttribute attributeDescription2 = commandElement.Attribute(XName.Get(@"descriptionTextResource")); //MLHIDE
            return new CommandInfo(
                commandName,
                commandType,
                commandParameterInfos,
                _resourceManager.GetDisplayString((string)attributeDisplayName),
                _resourceManager.GetDisplayString((string)attributeDescription2));
        }
    }
}